package com.fscp.follow.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.fscp.commons.constant.ApiConstant;
import com.fscp.commons.constant.RedisKeyConstant;
import com.fscp.commons.exception.ParameterException;
import com.fscp.commons.model.domain.ResultInfo;
import com.fscp.commons.model.pojo.Follow;
import com.fscp.commons.model.vo.ShortDinerInfo;
import com.fscp.commons.model.vo.SignInDinerInfo;
import com.fscp.commons.utils.AssertUtil;
import com.fscp.commons.utils.ResultInfoUtil;
import com.fscp.follow.mapper.FollowMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 关注/取关业务逻辑层
 */
@Service
public class FollowService {

    @Value("${service.name.ms-oauth-server}")
    private String oauthServerName;
    @Value("${service.name.ms-diner-server}")
    private String dinerServerName;
    @Resource
    private RestTemplate restTemplate;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private FollowMapper followMapper;


    /**
     * 关注/取关
     *
     * @param followDinerId
     * @param isFollowed    1:关注操作； 0：取关操作
     * @param accessToken
     * @param path
     * @return
     */
    public ResultInfo follow(Integer followDinerId, int isFollowed, String accessToken, String path) {

        // 是否选择了关注对象  dinerId >=1
        AssertUtil.isTrue(followDinerId == null || followDinerId < 1, "请选择要关注的人");
        // 获取登录用户信息（封装方法）
        SignInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);
        // 获取当前登录用户与需要关注用户的关注信息
        Follow follow = followMapper.selectFollow(dinerInfo.getId(), followDinerId);

        // 分三种情况
        // 1. 如果没有关注信息，且要进行关注操作 --- 添加关注
        if (follow == null && isFollowed == 1) {
            // 添加关注信息
            int count = followMapper.save(dinerInfo.getId(), followDinerId);
            // 添加到 Redis 关注列表
            if (count == 1) {
                addToRedisSet(dinerInfo.getId(), followDinerId);
            }
            return ResultInfoUtil.build(ApiConstant.SUCCESS_CODE, "关注成功",
                    path, "关注成功");
        }

        // 2. 如果有关注信息，且目前处于取关状态(is_valid == 0), 且要进行取关操作 --- 取消关注
        if (follow != null && follow.getIsValid() == 1 && isFollowed == 0) {
            // 取关
            int count = followMapper.update(follow.getId(), isFollowed);
            // 移出 Redis 关注列表
            if (count == 1) {
                removeFromRedisSet(dinerInfo.getId(), followDinerId);
            }
            return ResultInfoUtil.build(ApiConstant.SUCCESS_CODE, "取关成功",
                    path, "取关成功");
        }

        // 3. 如果有关注信息，且目前处于取关状态(is_valid == 0), 且要进行关注操作 --- 重新关注
        if (follow != null && follow.getIsValid() == 0 && isFollowed == 1) {
            // 重新关注
            int count = followMapper.update(follow.getId(), isFollowed);
            // 添加到 Redis 关注列表
            if (count == 1) {
                addToRedisSet(dinerInfo.getId(), followDinerId);
            }
            return ResultInfoUtil.build(ApiConstant.SUCCESS_CODE, "重新关注成功",
                    path, "重新关注成功");
        }

        // 以上三种情况都不进， 做一个集中返回处理
        return ResultInfoUtil.buildSuccess(path, "操作结束");
    }

    /**
     * 添加关注列表到 Redis
     *
     * @param dinerId
     * @param followDinerId
     */
    private void addToRedisSet(Integer dinerId, Integer followDinerId) {
        redisTemplate.opsForSet().add(RedisKeyConstant.following.getKey() + dinerId, followDinerId);
        redisTemplate.opsForSet().add(RedisKeyConstant.followers.getKey() + followDinerId, dinerId);
    }

    /**
     * 移出 Redis 关注列表
     *
     * @param dinerId
     * @param followDinerId
     */
    private void removeFromRedisSet(Integer dinerId, Integer followDinerId) {
        redisTemplate.opsForSet().remove(RedisKeyConstant.following.getKey() + dinerId, followDinerId);
        redisTemplate.opsForSet().remove(RedisKeyConstant.followers.getKey() + followDinerId, dinerId);
    }

    /**
     * 获取登录用户信息
     *
     * @param accessToken
     * @return
     */
    private SignInDinerInfo loadSignInDinerInfo(String accessToken) {
        // 必须登录
        AssertUtil.mustLogin(accessToken);
        String url = oauthServerName + "user/me?access_token={accessToken}";
        ResultInfo resultInfo = restTemplate.getForObject(url, ResultInfo.class, accessToken);
        if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) {
            throw new ParameterException(resultInfo.getMessage());
        }
        SignInDinerInfo dinerInfo = BeanUtil.fillBeanWithMap((LinkedHashMap) resultInfo.getData(),
                new SignInDinerInfo(), false);
        return dinerInfo;
    }

    /**
     * 共同关注列表
     *
     * @param dinerId           关注对象
     * @param accessToken       我
     * @param path
     * @return
     */
    public ResultInfo findCommonFriends(Integer dinerId, String accessToken, String path) {

        // 是否选择了关注对象（健壮性处理
        AssertUtil.isTrue(dinerId == null || dinerId < 1, "请选择要查看的人");
        // 获取登录用户信息
        SignInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);
        // 获取登录用户的关注信息
        String loginDinerKey = RedisKeyConstant.following.getKey() + dinerInfo.getId();
        // 获取登录用户的查看对象的关注信息
        String dinerKey = RedisKeyConstant.following.getKey() + dinerId;
        // 计算交集
        Set<Integer> dinerIds = redisTemplate.opsForSet().intersect(loginDinerKey, dinerKey);
        // 交集为空（健壮性
        if (dinerIds == null || dinerIds.isEmpty()) {
            return ResultInfoUtil.buildSuccess(path, new ArrayList<ShortDinerInfo>());
        }
        // 若非空。根据 ids 调用食客服务查询食客信息
        ResultInfo resultInfo = restTemplate.getForObject(dinerServerName + "findByIds?access_token={accessToken}&ids={ids}",
                ResultInfo.class, accessToken, StrUtil.join(",",dinerIds));
        // 判断本次远程调用请求是否成功
        if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) {
            resultInfo.setPath(path);
            return resultInfo;
        }
        // 处理结果集
        List<LinkedHashMap> dinerInfoMaps = (ArrayList) resultInfo.getData();
        // <LinkedHashMap> dinerInfoMaps = (List<LinkedHashMap>) resultInfo.getData();
        List<ShortDinerInfo> dinerInfos = dinerInfoMaps.stream()
                .map(diner -> BeanUtil.fillBeanWithMap(diner, new ShortDinerInfo(), true))
                .collect(Collectors.toList());

        return ResultInfoUtil.buildSuccess(path, dinerInfos);
    }

    /**
     * 获取粉丝列表 （Id 集合
     *
     * @param dinerId
     * @return
     */
    public Set<Integer> findFollowers(Integer dinerId) {

        AssertUtil.isNotNull(dinerId, "请选择要查看的用户");
        Set<Integer> followers = redisTemplate.opsForSet()
                .members(RedisKeyConstant.followers.getKey() + dinerId);

        return followers;
    }
}
